package com.zzyl.serve.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.bean.BeanUtils;
import com.zzyl.serve.domain.AlertData;
import com.zzyl.serve.domain.DeviceData;
import com.zzyl.serve.mapper.DeviceMapper;
import com.zzyl.serve.service.IAlertDataService;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.serve.mapper.AlertRuleMapper;
import com.zzyl.serve.domain.AlertRule;
import com.zzyl.serve.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 报警规则Service业务层处理
 * 
 * @author Lai
 * @date 2025-08-26
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysUserRoleMapper userRoleMapper;
    @Autowired
    private IAlertDataService alertDataService;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return alertRuleMapper.selectById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return alertRuleMapper.insert(alertRule);
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return alertRuleMapper.updateById(alertRule);
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return alertRuleMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return alertRuleMapper.deleteById(id);
    }

    /**
     * 过滤任务
     */
    @Override
    public void alertFilter() {
        //如果没有启用的警报规则，直接结束
        long count = count(Wrappers.<AlertRule>lambdaQuery().eq(AlertRule::getStatus, 1));
        if (count < 0) {
            return;
        }

        //如果找不到设备上报的数据，直接结束
        List<Object> list = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (ObjectUtil.isEmpty(list)) {
            return;
        }


        //解析设备上报的数据`
        //转换成List<DeviceData>
        List<DeviceData> deviceData = new ArrayList<>();
        list.forEach(o -> deviceData.addAll(JSONUtil.toList(o.toString(), DeviceData.class)));


        //处理设备上报的每条数据
        deviceData.forEach(deviceDatas -> dataProcessing(deviceDatas));

    }


    private void dataProcessing(DeviceData deviceData) {
        //如果设备数据距离上报时间超过一分钟，则不处理
        long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            return;
        }

        //查找此设备有效的所有规则，如果找不到就直接结束
        List<AlertRule> ruleList = list(Wrappers.<AlertRule>lambdaQuery()
                //针对此产品的规则
                .eq(AlertRule::getProductKey, deviceData.getProductKey())
                //针对此物理模型数据的规则
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                //针对此设备或针对此产品所有设备的规则
                .in(AlertRule::getIotId, "-1", deviceData.getIotId())
                //已启用规则
                .eq(AlertRule::getStatus, 1));

        if (ObjectUtil.isEmpty(ruleList)) {
            return;
        }
        //遍历每个规则，处理当前设备
        ruleList.forEach(alertRule -> deviceDataProcessing(alertRule, deviceData));
    }


    private void deviceDataProcessing(AlertRule alertRule, DeviceData deviceData) {
        //如果数据上报数据的数据不在上报有效时间内，则直接结束
        String[] period = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(period[0]);
        LocalTime end = LocalTime.parse(period[1]);
        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
        if (alarmTime.isBefore(start) || alarmTime.isAfter(end)) {
            return;
        }

        //准备规则异常持续周期缓存的key
        String alertCountCachekey = StringUtils.format(CacheConstants.ALERT_COUNT_KEY, deviceData.getIotId(), alertRule.getId());

        //如果设备没有到达报警规则的阈值，则清理异常次数缓存并结束
        if ((">=".equals(alertRule.getOperator()) && Double.valueOf(deviceData.getDataValue()) >= alertRule.getValue())
                || ("<".equals(alertRule.getOperator()) && Double.valueOf(deviceData.getDataValue()) < alertRule.getValue())) {
            log.error("设备数据符合报警阈值条件");
        } else {
            redisTemplate.delete(alertCountCachekey);
            return;
        }


        //如果规则在沉默周期内，则直接结束
        String alertSilentFlagKey = StringUtils.format(CacheConstants.ALERT_COUNT_KEY, deviceData.getIotId());
        Object silentNow = redisTemplate.opsForValue().get(alertSilentFlagKey);
        if (ObjectUtil.isNotEmpty(silentNow)) {
            return;
        }
        //如果未达到此规则的持续周期要求，则直接结束
        Long count = redisTemplate.opsForValue().increment(alertCountCachekey, 1);
        if (count == null || count < alertRule.getDuration()) {
            return;
        }

        //触发警报成功，删除警告触发次数缓存，进入沉默周期，
        redisTemplate.delete(alertCountCachekey);
        redisTemplate.opsForValue().set(alertSilentFlagKey, "1", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //找到报警的责任人
        Set<Long> userIds = new HashSet<>();
        if (alertRule.getAlertDataType() == 0) {

            //如果是老人异常数据，则找到老人的护理员
            if (ObjectUtil.equals(deviceData.getLocationType(), 0)) {
                //如果是随身设备的数据，设备在老人身上，根据设备id找到老人的护理员id
                userIds = deviceMapper.selectNurseIdsByElderIotId(deviceData.getIotId());
            } else if (ObjectUtil.equals(deviceData.getLocationType(), 1) && ObjectUtil.equals(deviceData.getPhysicalLocationType(), 2)) {
                //如果是固定设备，绑定在床位上，则要根据床位找到老人再找老人的护理员
                userIds = deviceMapper.selectNurseIdsByBedIotId(deviceData.getIotId());
            }
        } else if (alertRule.getAlertDataType() == 1) {
            //如果是设备异常数据，则要找到维修人员
            userIds = userRoleMapper.selectUserIdsByRoleName("维修工");
        }

        //每个问题都要发送给超级管理员、、
        Set<Long> admins = userRoleMapper.selectUserIdsByRoleName("超级管理员");
        userIds.addAll(admins);

        //保存报警数据
        batchSaveAlertData(userIds, alertRule, deviceData);
    }

    private void batchSaveAlertData(Set<Long> userIds, AlertRule alertRule, DeviceData deviceData) {
        List<AlertData> list = new ArrayList<>();
        //准备报警原因
        String reason = alertRule.getFunctionName() + alertRule.getOperator() + alertRule.getValue() + ",持续"
                + alertRule.getDuration() + "个周期,发出警报";


        //遍历每个报警的责任人
        for (Long userId : userIds) {
            //封装报警数据
            AlertData alertData = new AlertData();
            BeanUtils.copyProperties(deviceData, alertData);
            //将id置空，避免deviceData的id值拷贝给alertData的id属性
            alertData.setId(null);
            alertData.setAlertRuleId(alertRule.getId());
            alertData.setAlertReason(reason);
            alertData.setType(alertRule.getAlertDataType());
            //报警数据状态，0未处理
            alertData.setStatus(0);
            alertData.setUserId(userId);
            list.add(alertData);
        }
        alertDataService.saveBatch(list);
    }
}